home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
Development
/
General
/
GCC 1.37.1r15
/
Machines
/
out-tahoe.c
< prev
next >
Wrap
Text File
|
1990-03-14
|
13KB
|
551 lines
/* Subroutines for insn-output.c for Tahoe.
Copyright (C) 1989 Free Software Foundation, Inc.
This file is part of GNU CC.
GNU CC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
GNU CC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU CC; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* File: output-tahoe.c
*
* This port made at the University of Buffalo by Devon Bowen,
* Dale Wiles and Kevin Zachmann.
*
* Mail bugs reports or fixes to: gcc@cs.buffalo.edu
*/
/* most of the print_operand_address function was taken from the vax */
/* since the modes are basically the same. I had to add a special case, */
/* though, for symbol references with offsets. */
#include <stdio.h>
print_operand_address (file, addr)
FILE *file;
register rtx addr;
{
register rtx reg1, reg2, breg, ireg;
rtx offset;
static char *reg_name[] = REGISTER_NAMES;
retry:
switch (GET_CODE (addr))
{
case MEM:
fprintf (file, "*");
addr = XEXP (addr, 0);
goto retry;
case REG:
fprintf (file, "(%s)", reg_name [REGNO (addr)]);
break;
case PRE_DEC:
fprintf (file, "-(%s)", reg_name [REGNO (XEXP (addr, 0))]);
break;
case POST_INC:
fprintf (file, "(%s)+", reg_name [REGNO (XEXP (addr, 0))]);
break;
case PLUS:
reg1 = 0; reg2 = 0;
ireg = 0; breg = 0;
offset = 0;
if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
&& GET_CODE (XEXP (addr, 1)) == CONST_INT)
output_addr_const (file, addr);
if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
&& GET_CODE (XEXP (addr, 0)) == CONST_INT)
output_addr_const (file, addr);
if (CONSTANT_ADDRESS_P (XEXP (addr, 0))
|| GET_CODE (XEXP (addr, 0)) == MEM)
{
offset = XEXP (addr, 0);
addr = XEXP (addr, 1);
}
else if (CONSTANT_ADDRESS_P (XEXP (addr, 1))
|| GET_CODE (XEXP (addr, 1)) == MEM)
{
offset = XEXP (addr, 1);
addr = XEXP (addr, 0);
}
if (GET_CODE (addr) != PLUS)
;
else if (GET_CODE (XEXP (addr, 0)) == MULT)
{
reg1 = XEXP (addr, 0);
addr = XEXP (addr, 1);
}
else if (GET_CODE (XEXP (addr, 1)) == MULT)
{
reg1 = XEXP (addr, 1);
addr = XEXP (addr, 0);
}
else if (GET_CODE (XEXP (addr, 0)) == REG)
{
reg1 = XEXP (addr, 0);
addr = XEXP (addr, 1);
}
else if (GET_CODE (XEXP (addr, 1)) == REG)
{
reg1 = XEXP (addr, 1);
addr = XEXP (addr, 0);
}
if (GET_CODE (addr) == REG || GET_CODE (addr) == MULT)
{
if (reg1 == 0)
reg1 = addr;
else
reg2 = addr;
addr = 0;
}
if (offset != 0)
{
if (addr != 0) abort ();
addr = offset;
}
if (reg1 != 0 && GET_CODE (reg1) == MULT)
{
breg = reg2;
ireg = reg1;
}
else if (reg2 != 0 && GET_CODE (reg2) == MULT)
{
breg = reg1;
ireg = reg2;
}
else if (reg2 != 0 || GET_CODE (addr) == MEM)
{
breg = reg2;
ireg = reg1;
}
else
{
breg = reg1;
ireg = reg2;
}
if (addr != 0)
output_address (offset);
if (breg != 0)
{
if (GET_CODE (breg) != REG)
abort ();
fprintf (file, "(%s)", reg_name[REGNO (breg)]);
}
if (ireg != 0)
{
if (GET_CODE (ireg) == MULT)
ireg = XEXP (ireg, 0);
if (GET_CODE (ireg) != REG)
abort ();
fprintf (file, "[%s]", reg_name[REGNO (ireg)]);
}
break;
default:
output_addr_const (file, addr);
}
}
/* Do a quick check and find out what the best way to do the */
/* mini-move is. Could be a push or a move..... */
static char *
singlemove_string (operands)
rtx *operands;
{
if (GET_CODE (operands[0]) == MEM
&& GET_CODE (XEXP (operands[0],0)) == PRE_DEC)
return "pushl %1";
return "movl %1,%0";
}
/* given the rtx for an address, return true if the given */
/* register number is used in the address somewhere. */
int
regisused (addr,regnum)
rtx addr;
int regnum;
{
if (GET_CODE (addr) == REG)
{
if (REGNO (addr) == regnum)
return (1);
else
return (0);
}
if (GET_CODE (addr) == MEM)
return regisused (XEXP (addr,0),regnum);
if (GET_CODE (addr) == MULT || GET_CODE (addr) == PLUS)
return (regisused (XEXP (addr,0),regnum)
|| regisused (XEXP (addr,1),regnum));
return 0;
}
/* Given some rtx, traverse it and return the register used in a */
/* index. If no index is found, return 0. */
rtx
index_reg (addr)
rtx addr;
{
rtx temp;
if (GET_CODE (addr) == MEM)
return index_reg (XEXP (addr,0));
if (GET_CODE (addr) == MULT)
{
if (GET_CODE (XEXP (addr,0)) == REG)
return XEXP (addr,0);
else
return XEXP (addr,1);
}
if (GET_CODE (addr) == PLUS)
{
if (temp = index_reg (XEXP (addr,0)))
return temp;
else
return index_reg (XEXP (addr,1));
}
return 0;
}
/* simulate the move double by generating two movl's. You have */
/* to be careful about mixing modes here. A future improvement */
/* would be to allow immediate doubles. */
char *
output_move_double (operands)
rtx *operands;
{
enum { REGOP, OFFSOP, MEMOP, PUSHOP, POPOP, INDOP, CNSTOP, RNDOP } optype0, optype1;
rtx latehalf[2];
rtx shftreg0 = 0, shftreg1 = 0;
rtx temp0 = 0, temp1 = 0;
rtx addreg0 = 0, addreg1 = 0;
int dohighfirst = 0;
/* First classify both operands. */
if (REG_P (operands[0]))
optype0 = REGOP;
else if ((GET_CODE (operands[0])==MEM) && (shftreg0=index_reg (operands[0])))
optype0 = INDOP;
else if (offsettable_memref_p (operands[0]))
optype0 = OFFSOP;
else if (GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
{
optype0 = PUSHOP;
dohighfirst++;
}
else if (GET_CODE (operands[0]) == MEM)
optype0 = MEMOP;
else
optype0 = RNDOP;
if (REG_P (operands[1]))
optype1 = REGOP;
else if ((GET_CODE (operands[1])==MEM) && (shftreg1=index_reg (operands[1])))
optype1 = INDOP;
else if (offsettable_memref_p (operands[1]))
optype1 = OFFSOP;
else if (GET_CODE (XEXP (operands[1], 0)) == POST_INC)
optype1 = POPOP;
else if (GET_CODE (operands[1]) == MEM)
optype1 = MEMOP;
else if (GET_CODE (operands[1]) == CONST_DOUBLE || CONSTANT_P (operands[1]))
optype1 = CNSTOP;
else
optype1 = RNDOP;
/* set up for the high byte move for operand zero */
switch (optype0)
{
/* if it's a register, just use the next highest in the */
/* high address move. */
case REGOP:
latehalf[0] = gen_rtx (REG,SImode,REGNO (operands[0])+1);
break;
/* for an offsettable address, use the gcc function to */
/* modify the operand to get an offset of 4 higher for */
/* the second move. */
case OFFSOP:
latehalf[0] = adj_offsettable_operand (operands[0], 4);
break;
/* if the operand is MEMOP type, it must be a pointer */
/* to a pointer. So just remember to increase the mem */
/* location and use the same operand. */
case MEMOP:
latehalf[0] = operands[0];
addreg0 = XEXP (operands[0],0);
break;
/* if we're dealing with a push instruction, just leave */
/* the operand alone since it auto-increments. */
case PUSHOP:
latehalf[0] = operands[0];
break;
/* YUCK! Indexed addressing!! If the address is considered */
/* offsettable, go use the offset in the high part. Otherwise */
/* find what exactly is being added to the mutiplication. If */
/* it's a mem reference, increment that with the high part */
/* being unchanged to cause the shift. If it's a reg, do the */
/* same. If you can't identify it, abort. Remember that the */
/* shift register was already set during identification. */
case INDOP:
if (offsettable_memref_p (operands[0]))
{
latehalf[0] = adj_offsettable_operand (operands[0],4);
break;
}
latehalf[0] = operands[0];
temp0 = XEXP (XEXP (operands[0],0),0);
if (GET_CODE (temp0) == MULT)
{
temp1 = temp0;
temp0 = XEXP (XEXP (operands[0],0),1);
}
else
{
temp1 = XEXP (XEXP (operands[0],0),1);
if (GET_CODE (temp1) != MULT)
abort ();
}
if (GET_CODE (temp0) == MEM)
addreg0 = temp0;
else if (GET_CODE (temp0) == REG)
addreg0 = temp0;
else
abort ();
break;
/* if we don't know the operand type, print a friendly */
/* little error message... 8-) */
case RNDOP:
default:
abort ();
}
/* do the same setup for operand one */
switch (optype1)
{
case REGOP:
latehalf[1] = gen_rtx (REG,SImode,REGNO (operands[1])+1);
break;
case OFFSOP:
latehalf[1] = adj_offsettable_operand (operands[1], 4);
break;
case MEMOP:
latehalf[1] = operands[1];
addreg1 = XEXP (operands[1],0);
break;
case POPOP:
latehalf[1] = operands[1];
break;
case INDOP:
if (offsettable_memref_p (operands[1]))
{
latehalf[1] = adj_offsettable_operand (operands[1],4);
break;
}
latehalf[1] = operands[1];
temp0 = XEXP (XEXP (operands[1],0),0);
if (GET_CODE (temp0) == MULT)
{
temp1 = temp0;
temp0 = XEXP (XEXP (operands[1],0),1);
}
else
{
temp1 = XEXP (XEXP (operands[1],0),1);
if (GET_CODE (temp1) != MULT)
abort ();
}
if (GET_CODE (temp0) == MEM)
addreg1 = temp0;
else if (GET_CODE (temp0) == REG)
addreg1 = temp0;
else
abort ();
break;
case CNSTOP:
/* Since this machine is big-endian,
the late half must be the low-order word for an integer,
or the latter word for a float. */
if (GET_CODE (operands[1]) == CONST_DOUBLE)
{
if (GET_MODE_CLASS (GET_MODE (operands[1])) == MODE_FLOAT)
{
latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
CONST_DOUBLE_HIGH (operands[1]));
operands[1] = gen_rtx (CONST_INT, VOIDmode,
CONST_DOUBLE_LOW (operands[1]));
}
else
{
latehalf[1] = gen_rtx (CONST_INT, VOIDmode,
CONST_DOUBLE_LOW (operands[1]));
operands[1] = gen_rtx (CONST_INT, VOIDmode,
CONST_DOUBLE_HIGH (operands[1]));
}
}
else
{
latehalf[1] = operands[1];
operands[1] = const0_rtx;
}
break;
case RNDOP:
default:
abort ();
}
/* double the register used for shifting in both of the operands */
/* but make sure the same register isn't doubled twice! */
if (shftreg0 && shftreg1 && rtx_equal_p (shftreg0, shftreg1))
output_asm_insn ("addl2 %0,%0", &shftreg0);
else
{
if (shftreg0)
output_asm_insn ("addl2 %0,%0", &shftreg0);
if (shftreg1)
output_asm_insn ("addl2 %0,%0", &shftreg1);
}
/* if the destination is a register and that register is needed in */
/* the source addressing mode, swap the order of the moves since we */
/* don't want this destroyed til last. If both regs are used, not */
/* much we can do, so abort. If these becomes a problem, maybe we */
/* can do it on the stack? */
if (GET_CODE (operands[0])==REG && regisused (operands[1],REGNO (operands[0])))
if (regisused (latehalf[1],REGNO (latehalf[0])))
8;
else
dohighfirst++;
/* if we're pushing, do the high address part first. */
if (dohighfirst)
{
if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
output_asm_insn ("addl2 $4,%0", &addreg0);
else
{
if (addreg0)
output_asm_insn ("addl2 $4,%0", &addreg0);
if (addreg1)
output_asm_insn ("addl2 $4,%0", &addreg1);
}
output_asm_insn (singlemove_string (latehalf), latehalf);
if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
output_asm_insn ("subl2 $4,%0", &addreg0);
else
{
if (addreg0)
output_asm_insn ("subl2 $4,%0", &addreg0);
if (addreg1)
output_asm_insn ("subl2 $4,%0", &addreg1);
}
return singlemove_string (operands);
}
output_asm_insn (singlemove_string (operands), operands);
if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
output_asm_insn ("addl2 $4,%0", &addreg0);
else
{
if (addreg0)
output_asm_insn ("addl2 $4,%0", &addreg0);
if (addreg1)
output_asm_insn ("addl2 $4,%0", &addreg1);
}
output_asm_insn (singlemove_string (latehalf), latehalf);
if (addreg0 && addreg1 && (rtx_equal_p (addreg0,addreg1)))
output_asm_insn ("subl2 $4,%0", &addreg0);
else
{
if (addreg0)
output_asm_insn ("subl2 $4,%0", &addreg0);
if (addreg1)
output_asm_insn ("subl2 $4,%0", &addreg1);
}
if (shftreg0 && shftreg1 && (rtx_equal_p (shftreg0,shftreg1)))
output_asm_insn ("shar $1,%0,%0", &shftreg0);
else
{
if (shftreg0)
output_asm_insn ("shar $1,%0,%0", &shftreg0);
if (shftreg1)
output_asm_insn ("shar $1,%0,%0", &shftreg1);
}
return "";
}